#include "SampleSndFile.h"
#include <stdio.h>
#include <iostream>
#include <errno.h>
#include <string.h>

namespace libtrckr {
double *SampleSndFile::buff=0;
uint SampleSndFile::bsize=0;
int SampleSndFile::nb=0;

SampleSndFile::SampleSndFile(string filename):Sample(){
	
	_handler = sf_open(filename.c_str(), SFM_READ, _sfinfo);
	
	setBitrate( _sfinfo->samplerate);
	_lastreadcount=-1;//no read...
	_position=0;
	_format = string();
	nb++;
}

SampleSndFile::SampleSndFile(const char* filename){
	_sfinfo=(struct SF_INFO*)malloc(sizeof(struct SF_INFO));
	_handler = sf_open(filename, SFM_READ, _sfinfo);
	
	setBitrate( _sfinfo->samplerate);
	_lastreadcount=-1;//no read...
	_position=0;
	_format = string();
	nb++;
}

SampleSndFile::SampleSndFile(SampleSndFile &s){
	_handler=s._handler;
	_sfinfo->samplerate=s._sfinfo->samplerate;
	_sfinfo->frames=s._sfinfo->frames;
	_sfinfo->format=s._sfinfo->format;
	_sfinfo->channels=s._sfinfo->channels;
	_lastreadcount=-1;//no read...
	_position=0;
	_format = s._format;
	nb++;
}

SampleSndFile::~SampleSndFile(){
	free(_sfinfo);
	sf_close(_handler);
	nb--;
	if(nb==0 && buff!=0)
		free(buff);

}

bool SampleSndFile::isEOF( ) const{
	return _lastreadcount==0;
}

loff_t SampleSndFile::read(double *buffer,int count ){
	_lastreadcount=sf_readf_double(_handler, buffer, count);
	return _lastreadcount;
}

loff_t SampleSndFile::read(float *buffer, int count ){
	_lastreadcount=sf_readf_float(_handler, buffer, count);
	return _lastreadcount;
}

loff_t SampleSndFile::read(int *buffer, int count){
	_lastreadcount=sf_readf_int(_handler, buffer, count);
	return _lastreadcount;
}

loff_t SampleSndFile::read(short *buffer, int count){
	_lastreadcount=sf_readf_short(_handler, buffer, count);
	return _lastreadcount;
}
int SampleSndFile::channels() const {
	return _sfinfo->channels;
}
Buffer SampleSndFile::read(int count){
	if(buff==0){
		buff=(double*)malloc(sizeof(double)*count*_sfinfo->channels);
		bsize=count;
	}else if(bsize < count){
		free(buff);
		buff=(double*)malloc(sizeof(double)*count*_sfinfo->channels);
		bsize=count;
	}
	//double *d=(double*)malloc(sizeof(double)*count);
	read(buff,count);
	return Buffer(buff,_lastreadcount,count);
}

void SampleSndFile::read(Buffer &b){
	size_t count = b.capacity()-b.position();
	if(buff==0){
		buff=(double*)malloc(sizeof(double)*count*_sfinfo->channels);
		bsize=count;
	}else if(bsize < count){
		free(buff);
		buff=(double*)malloc(sizeof(double)*count*_sfinfo->channels);
		bsize=count;
	}
	//double *d=(double*)malloc(sizeof(double)*count);
	if(buff==NULL) std::cout << strerror(errno) << std::endl;
	if(b.channels()!=_sfinfo->channels) b.channels(_sfinfo->channels);
	_lastreadcount=read(buff,count);
	b.append(buff, _lastreadcount,_sfinfo->channels);
	//free(d);
}

string SampleSndFile::getFormat()  {
	if(_format.empty()){
		switch(_sfinfo->format & SF_FORMAT_TYPEMASK){
			case SF_FORMAT_WAV :
				_format=STR_WAV;
				break;
			case SF_FORMAT_AIFF :
				_format=STR_AIFF;
				break;
			case SF_FORMAT_AU :
				_format=STR_AU;
				break;
			case SF_FORMAT_RAW :
				_format=STR_RAW;
				break;
			case SF_FORMAT_PAF :
				_format=STR_PAF;
				break;
			case SF_FORMAT_SVX :
				_format=STR_SVX;
				break;
			case SF_FORMAT_NIST :
				_format=STR_NIST;
				break;
			case SF_FORMAT_VOC :
				_format=STR_VOC;
				break;
			case SF_FORMAT_IRCAM :
				_format=STR_IRCAM;
				break;
			case SF_FORMAT_W64 :
				_format=STR_W64;
				break;
			case SF_FORMAT_MAT4 :
				_format=STR_MAT4;
				break;
			case SF_FORMAT_MAT5 :
				_format=STR_MAT5;
				break;
			case SF_FORMAT_PVF :
				_format=STR_PVF;
				break;
			case SF_FORMAT_XI:
				_format=STR_XI;
				break;
			case SF_FORMAT_HTK:
				_format=STR_HTK;
				break;
			case SF_FORMAT_SDS:
				_format=STR_SDS;
				break;
			case SF_FORMAT_AVR :
				_format=STR_AVR;
				break;
			case SF_FORMAT_WAVEX :
				_format=STR_WAVEX;
				break;
			case SF_FORMAT_SD2 :
				_format=STR_SD2;
				break;
			case SF_FORMAT_FLAC:
				_format=STR_FLAC;
				break;
			case SF_FORMAT_CAF:
				_format=STR_CAF;
				break;
			#ifdef SF_FORMAT_WVE
			case SF_FORMAT_WVE:
				_format=STR_WVE;
				break;
			#endif
		}
		_format+=" ";
		switch(_sfinfo->format & SF_FORMAT_SUBMASK){
			case SF_FORMAT_PCM_S8 :
				_format+=STR_PCM_S8;
				break;
			case SF_FORMAT_PCM_16 :
				_format+=STR_PCM_16;
				break;
			case SF_FORMAT_PCM_24 :
				_format+=STR_PCM_24;
				break;
			case SF_FORMAT_PCM_32 :
				_format+=STR_PCM_32;
				break;
			case SF_FORMAT_PCM_U8 :
				_format+=STR_PCM_U8;
				break;
			case SF_FORMAT_FLOAT:
				_format+=STR_FLOAT;
				break;
			case SF_FORMAT_DOUBLE:
				_format+=STR_DOUBLE;
				break;
			case SF_FORMAT_ULAW :
				_format+=STR_ULAW;
				break;
			case SF_FORMAT_ALAW:
				_format+=STR_ALAW;
				break;
			case SF_FORMAT_IMA_ADPCM:
				_format+=STR_IMA_ADPCM;
				break;
			case SF_FORMAT_MS_ADPCM:
				_format+=STR_MS_ADPCM;
				break;
			case SF_FORMAT_GSM610 :
				_format+=STR_GSM610;
				break;
			case SF_FORMAT_VOX_ADPCM :
				_format+=STR_VOX_ADPCM;
				break;
			case SF_FORMAT_G721_32 :
				_format+=STR_G721_32;
				break;
			case SF_FORMAT_G723_24 :
				_format+=STR_G723_24;
				break;
			case SF_FORMAT_G723_40 :
				_format+=STR_G723_40;
				break;
			case SF_FORMAT_DWVW_12 :
				_format+=STR_DWVW_12;
				break;
			case SF_FORMAT_DWVW_16 :
				_format+=STR_DWVW_16;
				break;
			case SF_FORMAT_DWVW_24 :
				_format+=STR_DWVW_24;
				break;
			case SF_FORMAT_DWVW_N :
				_format+=STR_DWVW_N;
				break;
			case SF_FORMAT_DPCM_8 :
				_format+=STR_DPCM_8;
				break;
			case SF_FORMAT_DPCM_16 :
				_format+=STR_DPCM_16;
				break;
		}

		
	}
	return _format;	
}
bool SampleSndFile::seek(long where){
	return true;
}
void SampleSndFile::setFormat(int type, int sub, int endianness=0){
	//First free _format
	_format.clear();
	//format = type bitwiseOR sub bitwiseOR endianness
	_sfinfo->format=type | sub | endianness;
}

loff_t SampleSndFile::getLength( ) const {
	return _sfinfo->frames;
}

bool SampleSndFile::isValid() const {
	int error = sf_error(_handler);
	return error==SF_ERR_NO_ERROR;
}
}
